#!/usr/bin/perl 
# main - the entry of testing.  
use strict; 
use warnings; 
use URI::Heuristic; 
use HTTP::Request; 
use LWP::UserAgent; #use File::Tail; 

use constant{ 
	TARGET_URL		=> 'http://localhost:8080/arms-wab/hello?entry=', 	
	REQUEST_FLAG	=> 0, 
	RESPONSE_FLAG	=> 0, 
	
	REQUEST_METHOD_GET		=> 'GET', 
	REQUEST_METHOD_POST		=> 'POST', 
	REQUEST_METHOD_PUT		=> 'PUT', 
	REQUEST_METHOD_DELETE	=> 'DELETE', 
	REQUEST_METHOD_HEAD		=> 'HEAD', 

	TESTING_GET_HEADER			=> 'getHeader',
	TESTING_GET_HEADERS			=> 'getHeaders',
	TESTING_GET_PARAMETER_MAP	=> 'getParameterMap',
	TESTING_GET_PARAMETER_NAMES	=> 'getParameters',

	URL_EXTRA_PARA		=> 'extra_para',
	BENIGN				=> 'benign',	
	MALICIOUS_NO_HARM	=> '<thml>',
	MALICIOUS_HARM		=> '<script>',

	H_ACCEPT			=> 'accept',
	H_CONTENT_TYPE		=> 'content-type',
	H_X_REQUEST_WITH	=> 'x-request-with',
	H_COSTOMIZED		=> 'h',
	H_MALICIOUS			=> 'M-header',

	H_ACCEPT_1			=> 'text/html, */*; q=0.7',
	H_ACCEPT_2			=> 'text/xhtml, */*; q=0.5',
	H_ACCEPT_3			=> 'text/xml, */*; q=0.3',
	H_CONTENT_TYPE_1	=> 'text',
	H_CONTENT_TYPE_2	=> 'json',
	H_COSTOMIZED_1		=> 'h1',
	H_COSTOMIZED_2		=> 'h2',
	
	H_X_REQUEST_WITH_VALUE	=> 'xsrf cookie',

	PT_BENIGN	=> 'benign-a',	# benign input all places
	PT_M_URL	=> 'm-url',		# malicious input at url
	PT_M_H_NAME	=> 'm-h name',	# malicious input at http request header name
	PT_M_H_VALUE=> 'm-h value',	# malicious input at http request header value

	CONSOLE_PASS_ASSERTION	=> 'passed',
	CONSOLE_FAIL_ASSERTION	=> 'failed',
	
	USER_AGENT	=> "web-app-tester.pl"
};

sub send_request{	
	my $user_agent = LWP::UserAgent->new(); 
	$user_agent->agent("web-app-tester.pl"); 

	my $response = $user_agent->request($_[0]);
	if ($response->is_error()) {
		return $response->status_line;
	} else {
		return $response->content;
	} 
};

sub make_request{
	my($request_method, $url, $header) = @_;
	my $req = new HTTP::Request($request_method, $url, $header);
	return $req;
}

sub make_url{
	my($testing_target, $url_post_fix) = @_; #url_post_fix: bening, malicious no harm, malicious harm.
	
	my $url =  TARGET_URL . "$testing_target&" . URL_EXTRA_PARA . "=$url_post_fix"	or die "usage: $0 url\n";
#	print $url;
	return $url;
}

sub make_header{
	return init_header();
}

sub make_header_with_multientry{
	my $header = init_header();
	add_header_with_multientries($header);
	return $header;
}

sub make_header_with_malicious_value{
	my $header = make_header_with_multientry;
	add_header_with_malicious_value($header);
	return $header;
}

sub make_header_with_malicious_name{
	my $header = make_header_with_multientry;
	add_header_with_malicious_name($header);	
	return $header;
}

sub make_customized_header{
	my $extra_field = shift;
	my $header = init_header();
	return $header->push_header($extra_field);
}

sub init_header{
	my $header = new 
		HTTP::Headers(
			H_ACCEPT, H_ACCEPT_1, 
			H_CONTENT_TYPE, H_CONTENT_TYPE_1,
			H_COSTOMIZED, H_COSTOMIZED_1,	
			H_X_REQUEST_WITH, H_X_REQUEST_WITH_VALUE,
		);
	return $header;
}

sub add_header_with_multientries{
	my $header = shift;
	$header->push_header(
			H_ACCEPT, H_ACCEPT_2,
			H_ACCEPT, H_ACCEPT_3,	
			H_CONTENT_TYPE, H_CONTENT_TYPE_2,
			H_COSTOMIZED, H_COSTOMIZED_2,
		);
}
sub add_header_with_malicious_value{
	my $header = shift;
	$header->push_header(
			H_MALICIOUS, MALICIOUS_HARM,
		);
	return $header;
}

sub add_header_with_malicious_name{
	my $header = shift;
	$header->push_header(
			MALICIOUS_HARM, H_MALICIOUS
		);
}

sub assert_target{
	my($testing_target, $payload_type, $console_output) = @_;
	# $payload_tyep is not used because currently security exception is to log.
	if ($testing_target eq TESTING_GET_HEADER) {
		assert_get_header($console_output);
	}
	elsif ($testing_target eq TESTING_GET_HEADERS) {
		assert_get_headers($console_output);
	}else{}
}

sub do_assert{
	my ($console_output, %assert_header_map) = @_;
	for my $key (keys%assert_header_map){		
		#printf '[key: value] %s : %s', $key, $assert_header_map{$key};
		if ($console_output !~ /$assert_header_map{$key}/i) {
			return CONSOLE_FAIL_ASSERTION;
		}
	}
	return CONSOLE_PASS_ASSERTION;
}

sub assert_get_headers{
	my($console_output) = @_;
	
	my %assert_header_map = 
			(
				ac_assert => H_ACCEPT . ' : text\/html, \*\/\*; q=0\.7; text\/xhtml, \*\/\*; q=0\.5; text\/xml, \*\/\*; q=0\.3;',
				ct_assert => H_CONTENT_TYPE . ' : ' . H_CONTENT_TYPE_1 . '; ' . H_CONTENT_TYPE_2 . ';',
				h_assertion => H_COSTOMIZED . ' : ' . H_COSTOMIZED_1 . '; ' . H_COSTOMIZED_2 . ';',
			);
	do_assert($console_output, %assert_header_map);
}

sub assert_get_header{
	my($console_output) = @_;	
	my %assert_header_map = 
			(
				ac_assertion => 'accept : text\/html, \*\/\*; q=0\.7',
				ct_assert => H_CONTENT_TYPE . ' : ' . H_CONTENT_TYPE_1,
				h_assertion => H_COSTOMIZED . ' : ' . H_COSTOMIZED_1 ,
			);
	do_assert($console_output, %assert_header_map);
}


sub do_testing{
	my($request_method, $testing_target, $payload_type, $url_post_fix, $header) = @_;
	my $url = make_url($testing_target, $url_post_fix);
	my $req = make_request($request_method, $url, $header);
	my $response_content = send_request($req);


	if (REQUEST_FLAG) {
		printf "\n[Request]\n%s", $req->as_string;
	}

	if (RESPONSE_FLAG) {
		printf "\n[Response]\n%s\n", $response_content;
	}
	printf "%s\t%s\t%s\n", $testing_target, $payload_type, assert_target($testing_target, $payload_type, $response_content);
}

sub test_get_headers{
	do_testing(REQUEST_METHOD_GET, TESTING_GET_HEADERS, PT_BENIGN, BENIGN, make_header_with_multientry);
}

sub test_get_headers_with_m_h_name(){
	do_testing(REQUEST_METHOD_GET, TESTING_GET_HEADERS, PT_M_H_NAME, BENIGN, make_header_with_malicious_name);
}


sub test_get_headers_with_m_h_value(){
	do_testing(REQUEST_METHOD_GET, TESTING_GET_HEADERS, PT_M_H_VALUE, BENIGN, make_header_with_malicious_value);
}

sub test_get_header(){
	do_testing(REQUEST_METHOD_GET, TESTING_GET_HEADER, PT_BENIGN, BENIGN, make_header_with_multientry);
}

sub test_get_header_with_m_h_name{
	do_testing(REQUEST_METHOD_GET, TESTING_GET_HEADER, PT_M_H_NAME, BENIGN, make_header_with_malicious_name);
}

sub test_get_header_with_m_h_value{
	do_testing(REQUEST_METHOD_GET, TESTING_GET_HEADER, PT_M_H_VALUE, BENIGN, make_header_with_malicious_value);
}

sub main{
	print "\n[target]\t[payload]\t[output]\n";
	print "-----------------------------------------\n";
	test_get_header();
	test_get_header_with_m_h_name();	
	test_get_header_with_m_h_value();
	test_get_headers();
	test_get_headers_with_m_h_name();	
	test_get_headers_with_m_h_value();
};

main();

